using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

using FinancialAnalyzer.ToolClasses;
using FinancialAnalyzer.DataBase;

namespace FinancialAnalyzer.MoneyFlow
{
    public class MoneyFlowTreeHandler : IEnumerable
    {
        private MoneyFlowDB _moneyflowTree;
        private Stream _stream;
        private MemoryStream _mstream;
        private BinaryFormatter _bformatter;
        private bool _dirty;
        private string _currentFilename;
        public MoneyFlowTreeHandler(String TABLE_NAME_LEFT, String TABLE_NAME_RIGHT)
        {
            _mstream = new MemoryStream();
            _bformatter = new BinaryFormatter();
            _moneyflowTree = new MoneyFlowDB(TABLE_NAME_LEFT, TABLE_NAME_RIGHT);
            _dirty = false;
            _currentFilename = "ListSerDb.ogo";
        }

        public MoneyFlowTreeHandler(String filename, String TABLE_NAME_LEFT, String TABLE_NAME_RIGHT)
        {
            _mstream = new MemoryStream();
            _bformatter = new BinaryFormatter();
            _moneyflowTree = DeSerialize(filename) ?? new MoneyFlowDB(TABLE_NAME_LEFT, TABLE_NAME_RIGHT);
            _dirty = false;
            _currentFilename = filename;
        }


        #region Properties
        public List<MoneyFlowItem> ItemList
        {
            get { return _moneyflowTree.ItemList; }
        }
        public int CurrentMonth
        {
            get
            {
                return _moneyflowTree.CurrentMonth;
            }
        }
        public int CurrentYear
        {
            get
            {
                return _moneyflowTree.CurrentYear;
            }
        }
        public bool Dirty
        {
            get { return _dirty; }
        }
        public string CurrentFilename
        {
            get { return _currentFilename; }
            set { _currentFilename = value; }
        }
        #endregion

        #region Indexer
        public IMoneyFlowController this[EFlowDirection flowDir]
        {
            get
            {
                switch (flowDir)
                {
                    case EFlowDirection.Income:
                        return _moneyflowTree.IncomeController;
                    case EFlowDirection.Outcome:
                        return _moneyflowTree.OutcomeController;
                    default:
                        return null;
                }
            }
        }

        public IList<KeyValuePaar<Year, Double>> this[EFlowDirection flowDir, String classname]
        {
            get
            {
                IList<KeyValuePaar<Year, Double>> templist = null;
                switch (flowDir)
                {
                    case EFlowDirection.Income:
                        try
                        {
                            templist = _moneyflowTree.IncomeController.getListofLevel0(classname);
                        }
                        catch (CantGetListofLevelException e)
                        {
                            templist = null;
                            throw e;
                        }
                        break;
                    case EFlowDirection.Outcome:
                        try
                        {
                            templist = _moneyflowTree.OutcomeController.getListofLevel0(classname);
                        }
                        catch (CantGetListofLevelException e)
                        {
                            templist = null;
                            throw e;
                        }
                        break;
                }
                return templist;
            }
        }

        public IList<KeyValuePaar<Month, Double>> this[EFlowDirection flowDir, String classname, String year]
        {
            get
            {
                IList<KeyValuePaar<Month, Double>> templist = null;
                switch (flowDir)
                {
                    case EFlowDirection.Income:
                        try
                        {
                            templist = _moneyflowTree.IncomeController.getListofLevel1(classname, year);
                        }
                        catch (CantGetListofLevelException e)
                        {
                            templist = null;
                            throw e;
                        }
                        break; 
                    case EFlowDirection.Outcome:
                        try
                        {
                            templist = _moneyflowTree.OutcomeController.getListofLevel1(classname, year);
                        }
                        catch (CantGetListofLevelException e)
                        {
                            templist = null;
                            throw e;
                        }
                        break;
                }
                return templist;
            }
        }

        public IList<KeyValuePaar<String, Double>> this[EFlowDirection flowDir, String classname, String year, String month]
            {
                get
                {
                    IList<KeyValuePaar<String, Double>> templist = null;
                    switch (flowDir)
                    {
                        case EFlowDirection.Income:
                            try
                            {
                                templist = _moneyflowTree.IncomeController.getListofLevel2(classname, year, month);
                            }
                            catch (CantGetListofLevelException e)
                            {
                                templist = null;
                                throw e;
                            }
                            break;
                        case EFlowDirection.Outcome:
                            try
                            {
                                templist = _moneyflowTree.OutcomeController.getListofLevel2(classname, year, month);
                            }
                            catch (CantGetListofLevelException e)
                            {
                                templist = null;
                                throw e;
                            }
                            break;
                    }
                    return templist;
                }
            }
        #endregion

        #region Public Methods

        #region Find
        public IList<KeyValuePaar<String, Double>> Find(EFlowDirection flowDir, String searchString)
        {
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    return _moneyflowTree.IncomeController.Find(searchString);
                case EFlowDirection.Outcome:
                    return _moneyflowTree.OutcomeController.Find(searchString);
                default:
                    return null;
            }
        }

        public IList<KeyValuePaar<String, Double>> Find(EFlowDirection flowDir, String classname, String searchString)
        {
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    return _moneyflowTree.IncomeController[_moneyflowTree.IncomeController.IndexOf(classname)].Key.Find(searchString);
                case EFlowDirection.Outcome:
                    return _moneyflowTree.OutcomeController[_moneyflowTree.OutcomeController.IndexOf(classname)].Key.Find(searchString);
                default:
                    return null;
            }
        }

        public IList<KeyValuePaar<String, Double>> Find(EFlowDirection flowDir, String classname, String year, String searchString)
        {
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    Calendar cin = _moneyflowTree.IncomeController[_moneyflowTree.IncomeController.IndexOf(classname)].Key.Database;
                    return cin[cin.IndexOf(year)].Key.Find(searchString);
                case EFlowDirection.Outcome:
                    Calendar cout = _moneyflowTree.OutcomeController[_moneyflowTree.OutcomeController.IndexOf(classname)].Key.Database;
                    return cout[cout.IndexOf(year)].Key.Find(searchString);
                default:
                    return null;
            }
        }

        public IList<KeyValuePaar<String, Double>> Find(EFlowDirection flowDir, String classname, String year, String month, String searchString)
        {
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    Calendar cin = _moneyflowTree.IncomeController[_moneyflowTree.IncomeController.IndexOf(classname)].Key.Database;
                    Year yin = _moneyflowTree.IncomeController[_moneyflowTree.IncomeController.IndexOf(classname)].Key.Database[cin.IndexOf(year)].Key;
                    return yin[yin.IndexOf(month)].Key.Find(searchString);
                case EFlowDirection.Outcome:
                    Calendar cout = _moneyflowTree.OutcomeController[_moneyflowTree.OutcomeController.IndexOf(classname)].Key.Database;
                    Year yout = _moneyflowTree.OutcomeController[_moneyflowTree.OutcomeController.IndexOf(classname)].Key.Database[cout.IndexOf(year)].Key;
                    return yout[yout.IndexOf(month)].Key.Find(searchString);
                default:
                    return null;
            }
        }

        #endregion

        public void Add(EFlowDirection flowDir, String classname, String year, String month, String comment, Double value)
        {
            IMoneyFlowController controller = null;
            Year betroffenesJahr = null;
            _dirty = true;
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    String[] pathIn = { "Income", classname, year, month };
                    controller = _moneyflowTree.IncomeController;
                    betroffenesJahr = controller[classname, year];
                    //_incomeController.registerUndoState(pathIn, betroffenesJahr[betroffenesJahr.IndexOf(month)].Key);

                    controller.getListofLevel2(classname, year, month).Add(new KeyValuePaar<String, Double>(comment, value));
                    break;
                case EFlowDirection.Outcome:
                    String[] pathOut = { "Outcome", classname, year, month };
                    controller = _moneyflowTree.OutcomeController;
                    betroffenesJahr = controller[classname, year];
                    //_outcomeController.registerUndoState(pathOut, betroffenesJahr[betroffenesJahr.IndexOf(month)].Key);

                    controller.getListofLevel2(classname, year, month).Add(new KeyValuePaar<String, Double>(comment, value));
                    break;
            }
        }

        public void Delete(EFlowDirection flowDir, String classname, String year, String month, int index)
        {
            IMoneyFlowController controller = null;
            Year betroffenesJahr = null;
            _dirty = true;
            switch (flowDir)
            {
                case EFlowDirection.Income:
                    String[] pathIn = { "Income", classname, year, month };
                    controller = _moneyflowTree.IncomeController;
                    betroffenesJahr = controller[classname, year];
                    //_incomeController.registerUndoState(pathIn, betroffenesJahr[betroffenesJahr.IndexOf(month)].Key);

                    try
                    {
                        IList<KeyValuePaar<String, Double>> listIn = this[EFlowDirection.Income, classname, year, month];
                        listIn.RemoveAt(index);
                    }
                    catch (CantGetListofLevelException e)
                    {
                        throw e;
                    }
                    catch (ArgumentOutOfRangeException e)
                    {

                        throw e;
                    }
                    break;
                case EFlowDirection.Outcome:
                    String[] pathOut = { "Outcome", classname, year, month };
                    controller = _moneyflowTree.OutcomeController;
                    betroffenesJahr = controller[classname, year];
                    //_outcomeController.registerUndoState(pathOut, betroffenesJahr[betroffenesJahr.IndexOf(month)].Key);

                    try
                    {
                        IList<KeyValuePaar<String, Double>> listOut = this[EFlowDirection.Outcome, classname, year, month];
                        listOut.RemoveAt(index);
                    }
                    catch (CantGetListofLevelException e)
                    {
                        throw e;
                    }
                    catch (ArgumentOutOfRangeException e)
                    {
                        throw e;
                    }

                    break;
                default:
                    break;
            }
        }

        public void Sum(EFlowDirection eFlowDirection)
        {
            switch (eFlowDirection)
            {
                case EFlowDirection.Income:
                    _moneyflowTree.IncomeController.Sum();
                    break;
                case EFlowDirection.Outcome:
                    _moneyflowTree.OutcomeController.Sum();
                    break;
                default:
                    break;
            }
        }

        public void initController(List<MoneyFlowItem> iList, bool editedFlag)
        {
            _moneyflowTree.initController(iList);
        }

        public void checkDateChanged()
        {
            if (CurrentYear != DateTime.Today.Year)
            {
                _moneyflowTree.IncomeController.YearChanged(CurrentYear);
                _moneyflowTree.OutcomeController.YearChanged(CurrentYear);
            }
            else if (CurrentMonth != DateTime.Today.Month)
            {
                _moneyflowTree.IncomeController.MonthChanged(CurrentMonth);
                _moneyflowTree.OutcomeController.MonthChanged(CurrentMonth);
            }
            _moneyflowTree.refreshDate();
            _moneyflowTree.refreshController();
        }
        
        public void openDatabase(String path)
        {
            try
            {
                _moneyflowTree = DeSerialize(path);
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        #endregion

        #region Memento
        public void Serialize(String path)
        {
            try
            {
                _stream = File.Create(path, 256, FileOptions.WriteThrough);
                _bformatter.Serialize(_stream, _moneyflowTree);
                _dirty = false;
                _stream.Flush();
                _stream.Close();
            }
            catch (FileNotFoundException e)
            {
                throw e;
            }
            catch (SerializationException e)
            {
                throw e;
            }
        }

        private MoneyFlowDB DeSerialize(String path)
        {
            try
            {
                Stream stream = File.Open(path, FileMode.Open, FileAccess.Read);
                MoneyFlowDB output = (MoneyFlowDB)_bformatter.Deserialize(stream);
                stream.Close();
                return output;
            }
            catch (FileNotFoundException)
            {
                return null;
            }
            catch (SerializationException)
            {
                return null;
            }
        } 
        #endregion

        #region IEnumerable Member

        public IEnumerator GetEnumerator()
        {
            yield return _moneyflowTree.IncomeController;
            yield return _moneyflowTree.OutcomeController;
        }

        #endregion
     }
}
